package com.ccp.dev.workflow.service.bpm;

import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.db.datasource.JdbcTemplateUtil;
import com.ccp.dev.core.basic.table.TableModel;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.util.*;
import com.ccp.dev.form.model.FormField;
import com.ccp.dev.form.model.FormTable;
import com.ccp.dev.form.service.FormFieldService;
import com.ccp.dev.form.service.FormTableService;
import com.ccp.dev.form.util.FieldPool;
import com.ccp.dev.system.model.Dictionary;
import com.ccp.dev.system.model.SysRole;
import com.ccp.dev.system.service.DictionaryService;
import com.ccp.dev.system.util.SysRoleUtil;
import com.ccp.dev.workflow.dao.BpmBatchApprovalDao;
import com.ccp.dev.workflow.model.bpm.BpmBatchApproval;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;


/**
 * ClassDescribe:
 * 对象功能:流程批量审批定义设置 Service类
 *
 * @author :wangcheng
 * Date: 2019-10-08
 */
@Service
public class BpmBatchApprovalService extends BaseService<BpmBatchApproval> {
    @Resource
    private BpmBatchApprovalDao bpmBatchApprovalDao;
    @Resource
    private FormTableService formTableService;
    @Resource
    private DictionaryService dictionaryService;
    @Resource
    private FormFieldService formFieldService;

    /**
     * 判断某流程节点是否已经添加到批量审批设置
     *
     * @param bpmBatchApproval 批量审批定义对象
     * @return true: 流程节点已经配置过，false:没有配置
     */
    public boolean isExists(BpmBatchApproval bpmBatchApproval) {
        String id = bpmBatchApproval.getId();
        List<BpmBatchApproval> list = bpmBatchApprovalDao.isExists(bpmBatchApproval.getModelKey(), bpmBatchApproval.getNodeId(), id);
        return !list.isEmpty();
    }

    /**
     * 保存或者更新流程批量审批定义设置信息
     *
     * @param bpmBatchApproval 批量审批定义对象
     */
    public boolean save(BpmBatchApproval bpmBatchApproval) {
        String id = bpmBatchApproval.getId();
        if (StringUtil.isEmpty(id)) {
            bpmBatchApproval.setId(UUIDUtils.getUUIDFor32());
            this.add(bpmBatchApproval);
            return true;
        } else {
            this.update(bpmBatchApproval);
            return false;
        }
    }

    /**
     * 获取需要显示的字段说明
     *
     * @param batchApproval 批量审批定义配置对象
     * @return 字段描述列表
     */
    public List<String> getFieldList(BpmBatchApproval batchApproval) {
        // 字段设置
        String fieldJson = batchApproval.getFieldJson();
        // 获取需要显示的字段描述
        return this.getFieldList(fieldJson, "fieldDesc");
    }

    /**
     * 获取需要显示的字段名称
     *
     * @param batchApproval 批量审批定义配置对象
     * @return 字段名列表
     */
    public List<String> getFieldNameList(BpmBatchApproval batchApproval) {
        // 字段设置
        String fieldJson = batchApproval.getFieldJson();
        // 获取需要显示的字段名称
        return this.getFieldList(fieldJson, "fieldName");
    }

    /**
     * 获取需要显示的数据列表
     *
     * @param batchApproval 批量审批定义配置对象
     * @return 数据列表
     */
    public List<CaseInsensitiveMap> getDataList(BpmBatchApproval batchApproval, HttpServletRequest request) throws Exception {
        // 字段设置
        String fieldJson = batchApproval.getFieldJson();
        String nodeId = batchApproval.getNodeId();
        // 模型key
        String modelKey = batchApproval.getModelKey();
        // 表单id
        String tableId = batchApproval.getTableId();
        // 获取数据
        return this.getDataList(modelKey, nodeId,
                tableId, fieldJson, request);
    }

    /**
     * 获取数据列表
     * @param modelKey 模型key
     * @param nodeId 节点id
     * @param tableId 表单id
     * @param fieldJson 字段json数据
     * @return 数据列表
     */
    private List<CaseInsensitiveMap> getDataList(String modelKey, String nodeId, String tableId, String fieldJson, HttpServletRequest request) throws Exception {
        // 获取要查询的字段
        List<String> fieldList = this.getFieldList(fieldJson, "fieldName");
        if (BeanUtils.isEmpty(fieldList)) {
            return Collections.emptyList();
        }
        // 获取该流程、该节点有哪些待办的实例的关联业务key(关联表数据的id)
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("modelKey", modelKey);
        params.put("nodeId", nodeId);
        params.put("userId", ContextUtil.getCurrentUserId());
        //获取当前用户的可切换角色
        SysRole currentSysRole = SysRoleUtil.getCurrentRole(request);
        params.put("switchRoleId", currentSysRole.getRoleId());
        List<String> businessKeyList = bpmBatchApprovalDao.businessKeyList(params);

        // 　获取表对象
        FormTable bpmFormTable = formTableService
                .getByTableId(tableId, 1);

        String tableName = bpmFormTable.getTableName();
        // 是内部表，还是外部表
        int isExterNal = bpmFormTable.getIsExternal();
        // 表的主键
        String pkField = bpmFormTable.getPkField();
        // 数据源别名
        String dsAlias = bpmFormTable.getDsAlias();

        // 组装sql语句
        String sql = this.transformSql(fieldList, businessKeyList, tableName,
                pkField, isExterNal);

        // 查询数据
        List<Map<String, Object>> dataList = JdbcTemplateUtil
                .getNamedParameterJdbcTemplate(dsAlias).queryForList(sql,
                        new HashMap<>(10));

        List<CaseInsensitiveMap> insensitiveMapList = new ArrayList<>();
        // 格式化的数据
        if (dataList != null && !dataList.isEmpty()) {
            Map<String, Object> formatData = this.getFormatDataMap(
                    bpmFormTable, isExterNal);
            //解密主表数据
            List<FormField> encryptList = formFieldService.getEncryptFieldByTableId(tableId);
            encryptDecryptData(dataList, encryptList);
            dataList = this.formatDataList(dataList, formatData);
            // 将dataList里的Map转成忽略大小写的
            insensitiveMapList = insensitiveData(dataList);
        }

        return insensitiveMapList;
    }

    /**
     * 将数据Map转换成大小写不敏感的map
     * @param dataList 数据源
     * @return 修改后的数据
     */
    private List<CaseInsensitiveMap> insensitiveData(List<Map<String, Object>> dataList) {
        List<CaseInsensitiveMap> insensitiveMapList = new ArrayList<>();
        for (Map<String, Object> map : dataList) {
            insensitiveMapList.add(new CaseInsensitiveMap(map));
        }
        return insensitiveMapList;
    }

    /**
     * 解密数据
     * @param dataList 数据列表
     * @param encryptList 加密字段
     */
    private void encryptDecryptData(List<Map<String, Object>> dataList, List<FormField> encryptList) throws Exception {
        for (Map<String, Object> data : dataList) {
            for (FormField field : encryptList) {
                String decryptFieldName = field.getFieldName().toUpperCase();
                //如果包含这个加密字段key
                if (data.containsKey(decryptFieldName) && null != data.get(decryptFieldName) && !"".equals(decryptFieldName)) {
                    String classStr;
                    classStr = field.getDecryptClass();
                    if (StringUtil.isNotEmpty(classStr)) {
                        String[] ary = classStr.split("[.]");
                        if (ary != null) {
                            String beanId = ary[0];
                            String method = ary[1];
                            Object serviceBean = AppUtil.getBean(beanId);
                            if (serviceBean != null) {
                                Method invokeMethod = serviceBean.getClass().getDeclaredMethod(method, new Class[]{String.class});
                                String decryptVal = (String) invokeMethod.invoke(serviceBean, data.get(decryptFieldName).toString());
                                //放入加密/解密后的值
                                data.put(decryptFieldName, decryptVal);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 生成sql语句
     * @param fieldList 属性列表
     * @param businessKeyList 主键列表
     * @param tableName 表名
     * @param pkField 主键字段
     * @param isExterNal 是否是外部表
     * @return sql语句
     */
    private String transformSql(List<String> fieldList, List<String> businessKeyList, String tableName, String pkField, int isExterNal) {
        StringBuilder sbf = new StringBuilder();
        String userId = ContextUtil.getCurrentUserId();
        // 实例对象id
        String businessKeys = this.listToStrForPk(businessKeyList);
        String fields = "";
        if (FormTable.EXTERNAL == isExterNal) {
            // 如果是外部表，直接拼装
            fields = this.listToStr(fieldList, tableName);
        } else {
            // 如果不是外部表，就需要对字段和表名加上特定的前缀
            tableName = TableModel.CUSTOMER_TABLE_PREFIX + tableName;
            fields = this.listToStr(fieldList, tableName);
        }
        pkField = tableName + "." + pkField;
        sbf.append(
                " select task.ID_ as actTaskId ,task.DESCRIPTION_ AS actDescription , run.SUBJECT as actSubject, ")
                .append(fields)
                .append(" from ")
                .append(tableName)
                .append(" left join bpm_pro_run run on run.businessKey = ")
                .append(pkField)
                .append(" join act_ru_task task on task.proc_inst_id_=run.procInstId and task.DESCRIPTION_ in ('-1', '3', '21', '26','46')")
                .append(" and task.assignee_ = ")
                .append("'").append(userId).append("'")
                .append(" where ").append(pkField).append(" in (")
                .append(businessKeys).append(")");
        return sbf.toString();
    }

    /**
     * 格式化数据
     * @param dataList 数据列表
     * @param formatData 格式化数据
     * @return 格式化的数据列表
     */
    private List<Map<String, Object>> formatDataList(List<Map<String, Object>> dataList, Map<String, Object> formatData) {
        if (formatData.isEmpty()) {
            return dataList;
        }
        List<Map<String, Object>> rtDataList = new ArrayList<Map<String, Object>>();
        for (Map<String, Object> map : dataList) {
            for (String key : map.keySet()) {
                Object value = this
                        .getFormatData(map.get(key), key, formatData);
                map.put(key, value);
            }
            rtDataList.add(map);
        }
        return rtDataList;
    }

    /**
     * 获取数据格式化的值
     * @param object 格式化对象
     * @param name map的键
     * @param formatData 格式化数据
     * @return
     */
    private Object getFormatData(Object object, String name, Map<String, Object> formatData) {
        if (BeanUtils.isEmpty(object)) {
            return "";
        }
        // 如果是日期的
        if (object instanceof Date) {
            String style = StringPool.DATE_FORMAT_DATE;
            if (formatData.containsKey(name)) {
                Object format = formatData.get(name);
                if (BeanUtils.isNotEmpty(format)) {
                    style = (String) format;
                }
            }
            object = DateFormatUtil.format((Date) object, style);
        }
        // 如果是 下拉框，单选框的
        else if (formatData.containsKey(name)) {
            Object obj = formatData.get(name);
            if (BeanUtils.isNotEmpty(obj)) {
                if (obj instanceof Map) {
                    Map<?, ?> map = (Map<?, ?>) obj;
                    String[] selectStrs = object.toString().split(",");
                    //复选框多选
                    if(selectStrs.length>1){
                        String result = "";
                        for(String str : selectStrs){
                            result += map.get(str) + ",";
                        }
                        object = result.substring(0, result.length()-1);
                    }else{
                        object = map.get(object);
                    }
                    if (BeanUtils.isEmpty(object)) {
                        object = "";
                    }
                }
            }
        }
        return object.toString();
    }

    /**
     * 获取格式化的数据，目前只处理日期格式
     * @param bpmFormTable 属性表
     * @param isExterNal 是否是外部表
     * @return 格式化数据集合
     */
    private Map<String, Object> getFormatDataMap(FormTable bpmFormTable, int isExterNal) {
        Map<String, Object> map = new HashMap<>(16);
        // 字段信息
        List<FormField> fieldList = bpmFormTable.getFieldList();
        for (FormField bpmFormField : fieldList) {
            String name = bpmFormField.getFieldName().toUpperCase();
            String fieldType = bpmFormField.getFieldType();
            Short controlType = bpmFormField.getControlType();
            // 下面那些判断主要是为了字段值不能直接获取到的，例如日期，下拉框之类需要特殊处理的字段，那些输入框直接输入的就不用
            if (FormField.DATATYPE_DATE.equals(fieldType)) {
                map.put(name, bpmFormField.getDatefmt());
            } else {
                if (controlType == FieldPool.RADIO_INPUT || controlType == FieldPool.CHECKBOX) {
                    String options = bpmFormField.getJsonOptions();

                    if (StringUtils.isEmpty(options)) {
                        continue;
                    }

                    Map<String, String> optionMap = new HashMap<String, String>();
                    JSONArray jarray = JSONArray.fromObject(options);
                    for (Object obj : jarray) {
                        JSONObject json = (JSONObject) obj;
                        String key = (String) json.get("key");
                        String value = (String) json.get("value");
                        optionMap.put(key, value);
                    }
                    map.put(name, optionMap);
                } else if (controlType == FieldPool.SELECT_INPUT) {
                    // 单独处理下拉框，1、定义字段时定义了值，2、定义了下拉框级联的情况处理
                    // 1、处理普通情况，定义字段时的定义值
                    String options = bpmFormField.getJsonOptions();

                    Map<String, String> optionMap = new LinkedHashMap<String, String>();
                    if (StringUtil.isNotEmpty(options)) {
                        JSONArray jarray = JSONArray.fromObject(options);
                        for (Object obj : jarray) {
                            JSONObject json = (JSONObject) obj;
                            String key = (String) json.get("key");
                            String value = (String) json.get("value");
                            optionMap.put(key, value);
                        }
                    }
                    // 处理下拉框级联的情况
                    //this.handCascadeOpinion(formKey, name, optionMap);
                    map.put(name, optionMap);
                }else if(controlType == FieldPool.DICTIONARY){
                    //数据字典情况
                    String dicName = bpmFormField.getDictType();
                    List<Dictionary> dictionaryList = dictionaryService.getByNodeKey(dicName);
                    Map<String, String> dicMap = new HashMap<String, String>();
                    for(Dictionary dictionary:dictionaryList){
                        dicMap.put(dictionary.getItemName(), dictionary.getItemName());
                    }
                    map.put(name, dicMap);
                }
            }
        }
        return map;
    }

    /**
     * json转list，排除掉不显示的字段
     * @param fieldJson 属性字符串
     * @param dataKey 数据主键
     * @return
     */
    private List<String> getFieldList(String fieldJson, String dataKey) {
        List<String> list = new ArrayList<>();
        JSONArray jsonArray = JSONArray.fromObject(fieldJson);
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            if (BpmBatchApproval.IS_SHOW_Y.equals(obj.getString("isShow"))) {
                list.add(obj.getString(dataKey));
            }
        }
        return list;
    }

    /**
     * 字段list 转 str 中间以","隔开 每个字段都加上表名
     * @param fieldList 字段列表
     * @param tableName  表明
     * @return
     */
    private String listToStr(List<String> fieldList, String tableName) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < fieldList.size(); i++) {
            String fieldName = fieldList.get(i).toUpperCase();
            str.append(tableName).append(".").append(fieldName).append(" as ").append(fieldName);
            if (i + 1 < fieldList.size()) {
                str.append(",");
            }
        }
        return str.toString();
    }

    /**
     * 主键list 转 str 中间以","隔开，每个字段用''包围,防止主鍵不是number类型
     * @param businessKeyList 主键列表
     * @return
     */
    private String listToStrForPk(List<String> businessKeyList) {
        if (businessKeyList == null || businessKeyList.isEmpty()) {
            return "''";
        }
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < businessKeyList.size(); i++) {
            str.append("'").append(businessKeyList.get(i)).append("'");
            if (i + 1 < businessKeyList.size()) {
                str.append(",");
            }
        }
        return str.toString();
    }
}
